package com.dji.sample.wayline.controller;

import com.dji.sample.common.model.*;
import com.dji.sample.common.util.DjKmlUtil;
import com.dji.sample.common.util.KmlUtil;
import com.dji.sample.common.util.MinioUtil;
import com.dji.sample.component.event.DeviceStatusChangeEvent;
import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.wayline.model.dto.UploadKmlDto;
import com.dji.sample.wayline.model.dto.WaylineFileDTO;
import com.dji.sample.wayline.model.dto.WaylineFileUploadDTO;
import com.dji.sample.wayline.model.entity.WaylineInfoEntity;
import com.dji.sample.wayline.model.param.WaylineQueryParam;
import com.dji.sample.wayline.service.IWaylineFileService;
import io.minio.ObjectWriteResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import java.util.Random;

import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;

/**
 * @author sean
 * @version 0.3
 * @date 2021/12/22
 */
@RestController
@RequestMapping("${url.wayline.prefix}${url.wayline.version}/workspaces")
public class WaylineFileController {

    @Autowired
    private IWaylineFileService waylineFileService;

    @Autowired
    MinioUtil minioUtil;

    @Autowired
    DjKmlUtil kmlUtil;

    Random random = new Random();

    @Autowired
    private RedisOpsUtils redisOps;

    @Autowired
    IDeviceService deviceService;

    @PostMapping("/uploadKml")
    public ResponseResult<?> uploadKml(@RequestBody UploadKmlDto travelRecords, HttpServletRequest request) throws Exception {
        //生成航线
        boolean al = StringUtils.hasText(travelRecords.getTakeOffSecurityHeight());
        if (!al) {
            return ResponseResult.error("起飞高度参数异常");
        }
        boolean a2 = StringUtils.hasText(travelRecords.getEllipsoidHeight());
        if (!a2) {
            return ResponseResult.error("航线速度参数异常");
        }
        boolean a3 = StringUtils.hasText(travelRecords.getSpeed());
        if (!a3) {
            return ResponseResult.error("全局速度参数异常");
        }
        CustomClaim customClaim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);
        travelRecords.setOrgCode(customClaim.getOrgCode());
        travelRecords.setOrgId(customClaim.getOrgId());
        travelRecords.setUsername(customClaim.getUsername());
        if (travelRecords.getTravelRecordRequests().isEmpty()) {
            return ResponseResult.error("点位为空");
        }
        ObjectWriteResponse objectWriteResponse = kmlUtil.setTravelsKml(travelRecords.getTravelRecordRequests(), travelRecords.getTakeOffSecurityHeight(), travelRecords.getSpeed(), random.nextInt(65535), travelRecords.getEllipsoidHeight());
        if (objectWriteResponse == null) {
            return ResponseResult.error("上传失败");
        }
        waylineFileService.saveBatch(travelRecords, objectWriteResponse);
        return ResponseResult.success(objectWriteResponse.object());
    }


    @GetMapping("/new/page")
    public ResponseResult<PaginationData<WaylineInfoEntity>> newPage(@RequestParam Integer page,
                                                                     @RequestParam(name = "searchKey") String searchKey,
                                                                     @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                                                     @RequestParam(name = "deviceNo", required = false) String deviceNo, @RequestParam(name = "status", required = false) Integer status, HttpServletRequest request) {
        CustomClaim customClaim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);
        WaylineQueryParam param = WaylineQueryParam.builder()
                .page(page)
                .orgCode(customClaim.getOrgCode())
                .pageSize(pageSize)
                .orderBy("update_time desc")
                .searchKey(searchKey)
                .deviceNo(deviceNo)
                .status(status)
                .build();
        PaginationData<WaylineInfoEntity> data = waylineFileService.getWaylinesByParam(param);
        return ResponseResult.success(data);
    }

    @GetMapping("/device/page")
    public ResponseResult<PaginationData<WaylineFileDTO>> devicePage(@RequestParam Integer page,
                                                                     @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                                                     @RequestParam(name = "name", required = false) String name,
                                                                     @RequestParam(name = "type", required = false) Integer type,
                                                                     @RequestParam(name = "deviceNo", required = false) String deviceNo, @RequestParam(name = "status", required = false) Integer status, HttpServletRequest request) {
        CustomClaim customClaim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);
        WaylineQueryParam param = WaylineQueryParam.builder()
                .page(page)
                .pageSize(pageSize)
                .deviceNo(deviceNo)
                .status(status)
                .name(name)
                .type(type)
                .build();
        if (!StringUtils.hasText(deviceNo)) {
            param.setOrgCode(customClaim.getOrgCode());
        }
        PaginationData<WaylineFileDTO> data = waylineFileService.page(param);
        return ResponseResult.success(data);
    }

    @GetMapping("/getDetailById")
    public ResponseResult<?> getDetail(@RequestParam(name = "id") Long id) {
        return ResponseResult.success(waylineFileService.getDetail(id));
    }

    @PostMapping("/update")
    public ResponseResult<?> update(@RequestBody UploadKmlDto travelRecords, HttpServletRequest request) throws Exception {
        //生成航线
        if (travelRecords.getId() == null) {
            return ResponseResult.error("id必传");
        }
        boolean al = StringUtils.hasText(travelRecords.getTakeOffSecurityHeight()) && Integer.parseInt(travelRecords.getTakeOffSecurityHeight()) > 0;
        if (!al) {
            return ResponseResult.error("起飞高度参数异常");
        }
        boolean a2 = StringUtils.hasText(travelRecords.getEllipsoidHeight()) && Integer.parseInt(travelRecords.getEllipsoidHeight()) > 0;
        if (!a2) {
            return ResponseResult.error("航线速度参数异常");
        }
        boolean a3 = StringUtils.hasText(travelRecords.getSpeed()) && Integer.parseInt(travelRecords.getSpeed()) > 0;
        if (!a3) {
            return ResponseResult.error("全局速度参数异常");
        }
        CustomClaim customClaim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);
        travelRecords.setOrgCode(customClaim.getOrgCode());
        travelRecords.setOrgId(customClaim.getOrgId());
        travelRecords.setUsername(customClaim.getUsername());
        if (travelRecords.getTravelRecordRequests().isEmpty()) {
            return ResponseResult.error("点位为空");
        }
        waylineFileService.updateWayInfo(travelRecords);
        return ResponseResult.success("修改成功");
    }

    @PostMapping("/addDevice")
    public ResponseResult<?> addDevice(@RequestBody UploadKmlDto travelRecords, HttpServletRequest request) {
        if (travelRecords.getDeviceNo()==null || travelRecords.getDeviceNo().length==0) {
            return ResponseResult.error("设备不能为空");
        }
        if (travelRecords.getId() == null) {
            return ResponseResult.error("id 必传");
        }
        if(travelRecords.getDeviceNo().length>20){
            return ResponseResult.error("最多选择20个设备");
        }
        waylineFileService.addDevice(travelRecords.getDeviceNo(), travelRecords.getId());
        return ResponseResult.success("修改成功");
    }

    @PostMapping("/reduceDevice")
    public ResponseResult<?> reduceDevice(@RequestBody UploadKmlDto travelRecords, HttpServletRequest request) {
        if (travelRecords.getDeviceNo()==null || travelRecords.getDeviceNo().length==0) {
            return ResponseResult.error("设备不能为空");
        }
        if (travelRecords.getId() == null) {
            return ResponseResult.error("id 必传");
        }
        waylineFileService.reduceDevice(travelRecords.getDeviceNo(), travelRecords.getId());
        return ResponseResult.success("修改成功");
    }

    @GetMapping("/page")
    public ResponseResult<PaginationData<WaylineFileDTO>> page(@RequestParam Integer page,
                                                               @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                                               @RequestParam(name = "deviceNo", required = false) String deviceNo, @RequestParam(name = "status", required = false) Integer status, HttpServletRequest request) {
        CustomClaim customClaim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);
        WaylineQueryParam param = WaylineQueryParam.builder()
                .page(page)
                .orgCode(customClaim.getOrgCode())
                .pageSize(pageSize)
                .orderBy("update_time desc")
                .deviceNo(deviceNo)
                .status(status)
                .build();
        PaginationData<WaylineFileDTO> data = waylineFileService.getWaylinesByParam(null, param);
        return ResponseResult.success(data);
    }

    @PostMapping("/delete")
    public ResponseResult<?> delete(@RequestParam(name = "id") Long id,
                                    @RequestParam(name = "deviceNo") String deviceNo) {
        boolean bool = waylineFileService.deleteByIdAndDeviceNo(id, deviceNo);
        if (bool) {
            return ResponseResult.success("删除成功");
        }
        return ResponseResult.error("删除失败");
    }

    @PostMapping("/deleteById")
    public ResponseResult<?> deleteById(@RequestParam(name = "id") Long id) {
        boolean bool = waylineFileService.deleteLineInfo(id);
        if (bool) {
            return ResponseResult.success("删除成功");
        }
        return ResponseResult.error("删除失败");
    }

    @GetMapping("/getUrl")
    public ResponseResult<?> getUrl(@RequestParam(name = "objectKey") String objectKey) {
        return ResponseResult.success(minioUtil.preview(objectKey));
    }

    /**
     * Query the basic data of the wayline file according to the query conditions.
     * The query condition field in pilot is fixed.
     *
     * @param orderBy      Sorted fields. Spliced at the end of the sql statement.
     * @param favorited    Whether the wayline file is favorited or not.
     * @param page
     * @param pageSize
     * @param templateType
     * @param workspaceId
     * @return
     */
    @GetMapping("/{workspace_id}/waylines")
    public ResponseResult<PaginationData<WaylineFileDTO>> getWaylinesPagination(@RequestParam(name = "order_by") String orderBy,
                                                                                HttpServletRequest request,
                                                                                @RequestParam(required = false) boolean favorited, @RequestParam Integer page,
                                                                                @RequestParam(name = "page_size", defaultValue = "10") Integer pageSize,
                                                                                @RequestParam(name = "template_type", required = false) Integer[] templateType,
                                                                                @PathVariable(name = "workspace_id") String workspaceId) {
        WaylineQueryParam param = WaylineQueryParam.builder()
                .favorited(favorited)
                .page(page)
                .pageSize(pageSize)
                .orderBy(orderBy)
                .templateType(templateType)
                .build();
        CustomClaim customClaim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);
        StoreCache storeCache = CacheModel.get("waylineFileController").name("getWaylinesPagination");
        if (storeCache.containsKey(customClaim.getId())) {
            param.setDeviceNo(storeCache.get(customClaim.getId()).toString());
            PaginationData<WaylineFileDTO> data = waylineFileService.getWaylinesByParam(workspaceId, param);
            return ResponseResult.success(data);
        }
        redisOps.getAllKeys(RedisConst.DEVICE_ONLINE_PREFIX + "*").forEach(key -> {
            DeviceDTO device = (DeviceDTO) redisOps.get(key);
            if (customClaim.getId().equals(device.getUserId())) {
                //sn 换取no
                Optional<DeviceDTO> deviceDTO = deviceService.getDeviceBySn(device.getDeviceSn());
                if (deviceDTO.isPresent() && deviceDTO.get().getDomain().equals(DeviceDomainEnum.SUB_DEVICE.getDesc())) {
                    param.setDeviceNo(deviceDTO.get().getDeviceNo());
                    storeCache.put(customClaim.getId(), deviceDTO.get().getDeviceNo());
                    //30s 后删除
                    CacheModel.removeKeyDelay("waylineFileController", customClaim.getId(), 30L);
                }
            }
        });
        PaginationData<WaylineFileDTO> data = waylineFileService.getWaylinesByParam(workspaceId, param);
        return ResponseResult.success(data);
    }

    /**
     * Query the download address of the file according to the wayline file id,
     * and redirect to this address directly for download.
     *
     * @param workspaceId
     * @param waylineId
     * @param response
     */
    @GetMapping("/{workspace_id}/waylines/{wayline_id}/url")
    public void getFileUrl(@PathVariable(name = "workspace_id") String workspaceId,
                           @PathVariable(name = "wayline_id") String waylineId, HttpServletResponse response) {

        try {
            URL url = waylineFileService.getObjectUrl(workspaceId, waylineId);
            response.sendRedirect(url.toString());
        } catch (IOException | SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * When the wayline file is uploaded to the storage server by pilot,
     * the basic information of the file is reported through this interface.
     *
     * @param request
     * @param workspaceId
     * @param uploadFile
     * @return
     */
    @PostMapping("/{workspace_id}/upload-callback")
    public ResponseResult uploadCallBack(HttpServletRequest request,
                                         @PathVariable(name = "workspace_id") String workspaceId,
                                         @RequestBody WaylineFileUploadDTO uploadFile) {

        CustomClaim customClaim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);

        WaylineFileDTO metadata = uploadFile.getMetadata();
        metadata.setUsername(customClaim.getUsername());
        metadata.setObjectKey(uploadFile.getObjectKey());
        metadata.setName(uploadFile.getName());

        int id = waylineFileService.saveWaylineFile(workspaceId, metadata);

        return id <= 0 ? ResponseResult.error() : ResponseResult.success();
    }

    /**
     * Favorite the wayline file according to the wayline file id.
     *
     * @param workspaceId
     * @param ids         wayline file id
     * @return
     */
    @PostMapping("/{workspace_id}/favorites")
    public ResponseResult markFavorite(@PathVariable(name = "workspace_id") String workspaceId,
                                       @RequestParam(name = "id") List<String> ids) {
        boolean isMark = waylineFileService.markFavorite(workspaceId, ids, true);

        return isMark ? ResponseResult.success() : ResponseResult.error();
    }

    /**
     * Delete the favorites of this wayline file based on the wayline file id.
     *
     * @param workspaceId
     * @param ids         wayline file id
     * @return
     */
    @DeleteMapping("/{workspace_id}/favorites")
    public ResponseResult unmarkFavorite(@PathVariable(name = "workspace_id") String workspaceId,
                                         @RequestParam(name = "id") List<String> ids) {
        boolean isMark = waylineFileService.markFavorite(workspaceId, ids, false);

        return isMark ? ResponseResult.success() : ResponseResult.error();
    }

    /**
     * Checking whether the name already exists according to the wayline name must ensure the uniqueness of the wayline name.
     * This interface will be called when uploading waylines and must be available.
     *
     * @param workspaceId
     * @param names
     * @return
     */
    @GetMapping("/{workspace_id}/waylines/duplicate-names")
    public ResponseResult checkDuplicateNames(@PathVariable(name = "workspace_id") String workspaceId,
                                              @RequestParam(name = "name") List<String> names) {
        List<String> existNamesList = waylineFileService.getDuplicateNames(workspaceId, names);

        return ResponseResult.success(existNamesList);
    }

    /**
     * Delete the wayline file in the workspace according to the wayline id.
     *
     * @param workspaceId
     * @param waylineId
     * @return
     */
    @DeleteMapping("/{workspace_id}/waylines/{wayline_id}")
    public ResponseResult deleteWayline(@PathVariable(name = "workspace_id") String workspaceId,
                                        @PathVariable(name = "wayline_id") String waylineId) {
        boolean isDel = waylineFileService.deleteByWaylineId(workspaceId, waylineId);
        return isDel ? ResponseResult.success() : ResponseResult.error("Failed to delete wayline.");
    }


}
